%{

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <postgresql/libpq-fe.h>

PGconn *psql;

int yylex(void);
void yyerror(char *str);

char* strclone(const char *str)
{
    char *ret = malloc(strlen(str)+1);
    strcpy(ret, str);
    return ret;
}

struct word_t {
    char *word, *type, *root;
    struct word_t *next;
};

struct word_t* new_word(char *word, char *type, char *root)
{
    struct word_t *w = malloc(sizeof(struct word_t));

    w->word = strclone(word);
    w->type = strclone(type);
    w->root = strclone(root);
    w->next = 0;

    return w;
}


struct sn_t {
    struct word_t *adj1, *nc, *adj2;
    struct word_t *pre1, *pre2, *sp;
    struct word_t *pre0, *conj;
    struct sn_t *next;
};

struct sn_t* new_sn()
{
    return calloc(1, sizeof(struct sn_t));
}

void print_sql(char *word)
{
    char *qry=NULL, sane[101];

    PQescapeString(sane, word, strlen(word));
    asprintf(&qry, "select genero,numero from palavra where palavra=lower('%s')"
        " and (genero is not null or numero is not null)", sane);
    PGresult *res = PQexec(psql, qry);

    char gen = '?';
    char num = '?';

    if (res && PQntuples(res) > 0) {
        gen = PQgetvalue(res, 0, 0)[0];
        num = PQgetvalue(res, 0, 1)[0];
    }

    PQclear(res);

    printf("(gen=%c num=%c gra=%c)", gen, num, 'n');
}

void print_sn(struct sn_t *sn)
{
    struct word_t *w;

    printf("sintagma:");

    for (; sn; sn = sn->next) {
        if (sn->pre0) printf(" %s", sn->pre0->word);
        if (sn->pre1) printf(" %s", sn->pre1->word);
        if (sn->pre2) printf(" %s", sn->pre2->word);
        if (sn->adj1) printf(" %s", sn->adj1->word);

        printf(" [");

        //if (sn->nc) print_sql(sn->nc->word);

        for (w = sn->nc; w; w = w->next){
	    if(w->type) {
	            printf(" %s :%s:", w->word,w->type);
	    if(sn->nc)
		print_sql(w->word);
	}
        printf(" ]");

        if (sn->adj2) printf(" %s", sn->adj2->word);

        for (w = sn->sp; w; w = w->next)
            printf(" %s", w->word);

        if (sn->conj) printf(" %s", sn->conj->word);
    }
    printf("\n");
}

void printf2(char *s)
{
    //printf("%s", s);
}

%}

%token <w> VERBO CADA PRONOME_PESSOAL MESMO PRONOME_DEMONSTRATIVO
%token <w> PRONOME_INTERROGATIVO ADVERBIO NAO ADJETIVO PROPRIO PRONOME_OBLIQUO
%token <w> ARTIGO CONJUNCAO PREPOSICAO UM PRONOME_POSSESSIVO SE EN NOME OUTRO
%token <w> QUALQUER UNKNOWN PONTO VIRGULA PRONOME_INDEFINIDO

%type <w> pp_m_p_o maybe_m_p maybe_outro maybe_prep maybe_adjetivo maybe_artigo
%type <w> maybe_sp sp adverbio preposicao
%type <w> nomecomposto verbocomposto nome_real verbo_real
%type <sn> sn en sintagnominal

%start texto

%union {
    struct word_t *w;
    struct sn_t *sn;
};

%%

texto:
    | frase fim texto
    | error { yyerrok; } texto
    ;

fim:
    PONTO
    | VIRGULA
    ;

frase:
    oracao
    | oracao CONJUNCAO oracao
    | PRONOME_INTERROGATIVO oracao
    | sp oracao
    ;

oracao:
    sn sv   { print_sn($1); printf2("oracao: sn sv\n"); }
    | sv    { printf2("oracao: sv\n"); }
    | sn    { print_sn($1); printf2("oracao: sn\n"); }
    ;

sp:
    adverbio                        { $$ = $1; }
    | preposicao adverbio           { $$ = $1; $$->next = $2; }
    | preposicao adverbio adverbio  { $$ = $1; $$->next = $2; $2->next = $3; }
    ;

sv:
    expverbal           { printf2("sv: expverbal\n"); }
    | expverbal sn      { print_sn($2); printf2("sv: expverbal sn\n"); }
    | expverbal sn sn   { print_sn($2); print_sn($3); printf2("sv: expverbal sn sn\n"); }
    ;

expverbal:
    verbo           { printf2("expverbal: verbo\n"); }
    | SE verbo      { printf2("expverbal: SE verbo\n"); }
    | NAO SE verbo  { printf2("expverbal: NAO SE verbo\n"); }
    ;

verbo:
    maybe_sp verbocomposto maybe_sp { printf2("verbo: maybe_sp verbocomposto maybe_sp\n"); }
    | maybe_sp verbocomposto CONJUNCAO verbocomposto maybe_sp
    ;

verbocomposto:
    verbo_real verbocomposto    { $$ = $1; $$->next = $2; }
    | verbo_real                { $$ = $1; }
    ;

verbo_real:
    VERBO           { $$ = $1; }
    | UNKNOWN       { $$ = $1; }
    ;

nomecomposto:
    nome_real nomecomposto      { $$ = $1; $$->next = $2; }
    | nome_real                 { $$ = $1; }
    ;

nome_real:
    NOME            { $$ = $1; }
    | UNKNOWN       { $$ = $1; }
    ;

sn:
    maybe_prep sintagnominal    {
        $$ = $2;
        $$->pre0 = $1;
        printf2("sn: maybe_prep sintagnominal\n");
    }

    | maybe_prep sintagnominal CONJUNCAO sintagnominal  {
        $$ = $2;
        $$->pre0 = $1;
        $$->conj = $3;
        $$->next = $4;
        printf2("sn: maybe_prep sintagnominal CONJUNCAO sintagnominal\n");
    }
    ;

en:
    maybe_adjetivo nomecomposto maybe_adjetivo {
        $$ = new_sn();
        $$->adj1 = $1;
        $$->nc = $2;
        $$->adj2 = $3;
        printf2("en: maybe_adjetivo nomecomposto maybe_adjetivo\n");
    }
    ;

sintagnominal:
    PRONOME_PESSOAL             { $$ = new_sn(); $$->pre1 = $1; }
    | maybe_artigo en maybe_sp  { $$ = $2; $$->pre1 = $1; $$->sp = $3; printf2("sintagnominal: maybe_artigo en maybe_sp\n"); }
    | maybe_artigo pp_m_p_o en  { $$ = $3; $$->pre1 = $1; $$->pre2 = $2; }
    | PRONOME_INDEFINIDO en     { $$ = $2; $$->pre1 = $1; }
    | QUALQUER UM               { $$ = new_sn(); $$->pre1 = $1; $$->pre2 = $2; }
    | CADA UM                   { $$ = new_sn(); $$->pre1 = $1; $$->pre2 = $2; }
    | QUALQUER maybe_outro en   { $$ = $3; $$->pre1 = $1; $$->pre2 = $2; }
    | PRONOME_DEMONSTRATIVO     { $$ = new_sn(); $$->pre1 = $1; }
    | PRONOME_OBLIQUO maybe_m_p { $$ = new_sn(); $$->pre1 = $1; $$->pre2 = $2; }
    ;

pp_m_p_o:
    PRONOME_POSSESSIVO  { $$ = $1; }
    | MESMO             { $$ = $1; }
    | PROPRIO           { $$ = $1; }
    | OUTRO             { $$ = $1; }
    ;

maybe_m_p:              { $$ = 0; }
    | MESMO             { $$ = $1; }
    | PROPRIO           { $$ = $1; }
    ;

maybe_outro:            { $$ = 0; }
    | OUTRO             { $$ = $1; }
    ;

maybe_prep:             { $$ = 0; }
    | PREPOSICAO        { $$ = $1; }
    ;

maybe_adjetivo:         { $$ = 0; }
    | ADJETIVO          { $$ = $1; }
    ;

maybe_artigo:           { $$ = 0; }
    | ARTIGO            { $$ = $1; }
    ;

maybe_sp:               { $$ = 0; }
    | sp                { $$ = $1; }
    ;

preposicao:
    PREPOSICAO          { $$ = $1; }
    ;

adverbio:
    ADVERBIO            { $$ = $1; }
    ;

%%

struct {
    const char *str;
    int ret;
} types[] = {
    { "VIRG",   VIRGULA },
    { "SENT",   PONTO },
    { "V",      VERBO },
    { "NOM",    NOME },
    { "ADJ",    ADJETIVO },
    { "PRP",    PREPOSICAO },
    { "DET",    ARTIGO },
    { "ADV",    ADVERBIO },
    { 0, 0 }
};

int yylex(void)
{
    char line[80];

    if (!fgets(line, 80, stdin)) return 0;

    char *word = strtok(line, "\t\n");
    char *type = strtok(0, "\t\n");
    char *root = strtok(0, "\t\n");

    yylval.w = new_word(word, type, root);

//    printf("%s\n", word);

    if (!strcasecmp(word, "se"))
        return SE;

    int i;
    for (i=0; types[i].str; i++)
        if (!strcmp(type, types[i].str))
            return types[i].ret;

    return UNKNOWN;
}

void yyerror(char *str)
{
    printf("error: %s\n", str);
}


int main(int argc, char *argv[])
{
    psql = PQconnectdb("host='localhost' dbname='lexicon' user='sergio' password='123'");
    if (!psql || PQstatus(psql) != CONNECTION_OK) {
        fprintf(stderr, "libpq error: %s\n", PQerrorMessage(psql));
        exit(0);
    }
	
	if(argc){
		stdin=fopen(argv[1],"rw");
		printf("stdin changed..\n");
	}
    yyparse();

    return 0;
}
